import { getBusinessObject, is, isAny } from 'bpmn-js/lib/util/ModelUtil'
import { isExpanded, isEventSubProcess, isInterrupting } from 'bpmn-js/lib/util/DiUtil'
import { CONDITIONAL_SOURCES, ENGINE } from './constants'
import store from '../store'
import { splitColon } from './tools'

export const
  customize = (suffix) => {
    return ENGINE + ':' + suffix
  },
  isConditionalSource = (bo) => {
    return isAny(bo, CONDITIONAL_SOURCES)
  },
  isExpression = (conditionType) => {
    return conditionType && conditionType === 'expression'
  },
  isScript = (conditionType) => {
    return conditionType && conditionType === 'script'
  },
  isResource = (scriptType) => {
    return scriptType && scriptType === 'resource'
  },
  getRoot = (element) => {
    let p = getBusinessObject(element)
    while (p['$parent']) {
      p = p['$parent']
    }
    return p
  },
  filterElementsByType = (objectList, type) => {
    return objectList ? objectList.filter(obj => is(obj, type)) : []
  },
  findRootElementsByType = (element, referencedType) => {
    const root = getRoot(element)
    return filterElementsByType(root['rootElements'], referencedType)
  },
  getFlowElements = (element, type) => {
    return filterElementsByType(element['flowElements'], type)
  },
  getProcessElement = (modeler) => {
    return modeler.getDefinitions().rootElements.find(item => is(item, 'bpmn:Process'))
  },
  isInOut = (element, binding) => {
    if (binding.type === 'camunda:in') {
      // find based on target attribute
      if (binding.target) {
        return element.target === binding.target
      }
    }

    if (binding.type === 'camunda:out') {
      // find based on source / sourceExpression
      if (binding.source) {
        return element.source === binding.source
      }

      if (binding.sourceExpression) {
        return element.sourceExpression === binding.sourceExpression
      }
    }

    // find based variables / local combination
    if (binding.variables) {
      return element.variables === 'all' && (
        binding.variables !== 'local' || element.local
      )
    }
  },
  /**
   * 不使用value的IO variable
   * @param binding
   * @returns {boolean}
   */
  isInOutVariable = (binding) => {
    return (binding.variables === 'local' || binding.variables === 'all') &&
      ((binding.type === 'camunda:in' && !binding.target && !binding.expression) ||
        (binding.type === 'camunda:out' && !binding.source && !binding.sourceExpression))
  },
  isInputOutputSupported = (businessObject) => {
    return (
      is(businessObject, 'bpmn:FlowNode') && !(
        isAny(businessObject, ['bpmn:StartEvent', 'bpmn:BoundaryEvent', 'bpmn:Gateway']) ||
        is(businessObject, 'bpmn:SubProcess') && businessObject.get('triggeredByEvent')
      )
    )
  },
  isPropertyVisible = (entryId, bo) => {
    if (!bo || !bo['modelerTemplate']) {
      return true
    }
    const template = store.getters.getTemplates(bo.$type).find(t => t.id === bo['modelerTemplate'])
    if (template && template.properties) {
      const property = template.properties.find(property => {
        const binding = property['binding']
        return binding && binding.type === 'property' && splitColon(binding.name) === entryId
      })
      return property === undefined && isEntryVisible(entryId, template['entriesVisible'])
    }
    return true
  },
  isEntryVisible = (entryId, _entriesVisible) => {
    const
      entriesVisible = _entriesVisible || { _all: false },
      defaultVisible = entriesVisible._all,
      entryVisible = entriesVisible[ entryId ]

    if (typeof entriesVisible === 'boolean') {
      return entriesVisible
    }

    if (defaultVisible) {
      return entryVisible !== false
    } else {
      return entryVisible === true
    }
  },
  findOutputParameter = (inputOutput, binding) => {
    const outputParameters = inputOutput.get('outputParameters')
    return outputParameters && outputParameters.find(p => {
      if (!binding.scriptFormat) {
        return p.value === binding.source
      }
      if (!p.definition || binding.scriptFormat !== p.definition.scriptFormat) {
        return false
      }
      return p.definition.value === binding.source
    })
  },
  // https://github.com/bpmn-io/bpmn-js-properties-panel/tree/master/src/render/PanelHeaderProvider.js
  getConcreteType = (element) => {
    const
      { type: elementType } = element,
      // (1) event definition types
      eventDefinition = getEventDefinition(element)

    let type = getRawType(elementType)

    if (eventDefinition) {
      type = `${getEventDefinitionPrefix(eventDefinition)}${type}`

      // (1.1) interrupting / non interrupting
      if (
        (is(element, 'bpmn:StartEvent') && !isInterrupting(element)) ||
        (is(element, 'bpmn:BoundaryEvent') && !isCancelActivity(element))
      ) {
        type = `${type}NonInterrupting`
      }

      return type
    }

    // (2) sub process types
    if (is(element, 'bpmn:SubProcess') && !is(element, 'bpmn:Transaction')) {
      if (isEventSubProcess(element)) {
        type = `Event${type}`
      } else {
        const expanded = isExpanded(element) && !isPlane(element)
        type = `${expanded ? 'Expanded' : 'Collapsed'}${type}`
      }
    }

    // (3) conditional + default flows
    if (isDefaultFlow(element)) {
      type = 'DefaultFlow'
    }

    if (isConditionalFlow(element)) {
      type = 'ConditionalFlow'
    }

    return type
  },
  isCancelActivity = (element) => {
    const businessObject = getBusinessObject(element)

    return businessObject && businessObject['cancelActivity'] !== false
  },
  getEventDefinition = (element) => {
    const businessObject = getBusinessObject(element)
    return businessObject && businessObject['eventDefinitions'] && businessObject['eventDefinitions'][0]
  },
  getRawType = (type) => {
    return type.split(':')[1]
  },
  getEventDefinitionPrefix = (eventDefinition) => {
    const rawType = getRawType(eventDefinition.$type)

    return rawType.replace('EventDefinition', '')
  },
  isDefaultFlow = (element) => {
    const businessObject = getBusinessObject(element), sourceBusinessObject = getBusinessObject(element.source)

    if (!is(element, 'bpmn:SequenceFlow') || !sourceBusinessObject) {
      return false
    }

    return sourceBusinessObject.default && sourceBusinessObject.default === businessObject && (
      is(sourceBusinessObject, 'bpmn:Gateway') || is(sourceBusinessObject, 'bpmn:Activity')
    )
  },
  isConditionalFlow = (element) => {
    const businessObject = getBusinessObject(element), sourceBusinessObject = getBusinessObject(element.source)

    if (!is(element, 'bpmn:SequenceFlow') || !sourceBusinessObject) {
      return false
    }

    return businessObject['conditionExpression'] && is(sourceBusinessObject, 'bpmn:Activity')
  },
  isPlane = (element) => {
    // Backwards compatibility for bpmn-js<8
    const di = element && (element.di || getBusinessObject(element)['di'])

    return is(di, 'bpmndi:BPMNPlane')
  }
